Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Send a configurable CSP in every HTML response #9665

Open
wants to merge 15 commits into
base: master
Choose a base branch
from

Conversation

pabzm
Copy link
Member

@pabzm pabzm commented Oct 7, 2024

The CSP gets adapted to remote objects being allowed or not. It can be configured or disabled via the config option content_security_policy (and
content_security_policy_add_allow_remote).

The default is pretty weak in order to not introduce a breaking change, but still not useless (e.g. it adds another layer of defence against loading remote objects unless allowed.)

Closes #9638

@pabzm
Copy link
Member Author

pabzm commented Oct 22, 2024

@alecpl Same question for this: would reviews by other developers help you?

@alecpl
Copy link
Member

alecpl commented Oct 22, 2024

I'm not sure it would help me, but it would likely help the pull request move forward.

@pabzm
Copy link
Member Author

pabzm commented Oct 23, 2024

Are there other things we can do to ease your load? E.g. get more ticket work off your back? Or release/version/patch-management? I notice you're backporting many changes that go into the master-branch to one or two other branches, I guess that's a lot to juggle, too.

@pabzm
Copy link
Member Author

pabzm commented Oct 23, 2024

@johndoh, @mvorisek Do you feel competent to review this PR? It would help us a lot, even if you're not perfectly sure about all parts, just tell us about your opinion.

$csp = $this->app->config->get('content_security_policy');
if (!in_array($csp, ['', false, 'false'])) {
$csp_header = "Content-Security-Policy: {$csp}";
if (isset($this->env['safemode']) && $this->env['safemode'] === true) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs E2E test as it is it can very easily break security if coded wrongly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing is a good point 👍

Do you have something specific in mind regarding breaking security?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean we need to be sure external resources are not fetched (otherwise spammers know what email addresses to spam even more) in every other case than explicitly configured so.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but I think this change doesn't enlarge that danger. It only adds a Content-Security-Policy, which should reduce that danger. The safe-flag is not touched.

Do you see any way in which this change could be dangerous?

@alecpl
Copy link
Member

alecpl commented Oct 24, 2024

I'm not convinced that providing such configuration options is a good idea.

@johndoh
Copy link
Contributor

johndoh commented Oct 25, 2024

I know nothing about CSP headers. From a Roundcube code style point of view only I would say that

if (!in_array($csp, ['', false, 'false'])) {

is wrong and it should be

if ($csp = $this->app->config->get('content_security_policy', "default-src 'self' data: blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';")) {

this means, i think, in common with other config values null reverts to a hard coded default and false disables (e.g imap_ns_*).

I would also say the comment in defaults.inc.php needs to mention that content_security_policy must end in a ; or the header will be invalid when concatenated with content_security_policy_add_allow_remote

@pabzm
Copy link
Member Author

pabzm commented Oct 28, 2024

I'm not convinced that providing such configuration options is a good idea.

@alecpl Why? I thought some people might need to change the CSP (e.g. for plugins using external resources), and this way they wouldn't need to patch the code.

@pabzm
Copy link
Member Author

pabzm commented Oct 28, 2024

I know nothing about CSP headers.

@johndoh But your comments are helpful, thank you very much!

this means, i think, in common with other config values null reverts to a hard coded default and false disables

For some values that's right, but defaults.inc.php has a lot of actual default values, too. E.g. most booleans, but also oauth_user_create_map, or blankpage_url.
I would like to avoid repeating the default value, because it's pretty complex in this case – but at the same time it needs to be visible outside of the actual code as a reference. Thus I find the other way more appropriate.
Is there a declared will or style guide towards one of the default value methods?

content_security_policy must end in a ;

Thank you for that catch! I'l solve that in code so admins can't break the CSP so easily by mistake.

The CSP gets adapted to remote objects being allowed or not.
It can be configured or disabled via the config option
`content_security_policy` (and
`content_security_policy_add_allow_remote`).
@pabzm pabzm force-pushed the always-send-csp branch 2 times, most recently from ba0ab41 to a9e0110 Compare October 29, 2024 20:12
@johndoh
Copy link
Contributor

johndoh commented Oct 29, 2024

I would like to avoid repeating the default value,

I agree with this.

I think in the past there has been issues with defaults.inc.php not being properly updated in 3rd party packaged versions of Roundcube (which I acknowledge are not our problem) and people affected by that create issues here. The comment could suggest a value while the code has a reliable default so if defaults.inc.php was not updated there is no unpredictable behavior.

Personally I just don't like the in_array check, I think its unnecessary because it handles the edge case where someone set 'false' vs false and I think in general Roundcube does not do that so why do it for this one var.

If people write a lax default CSP they might set the additional config
option to the blank string, or false. Then the CSP header should not
contain that value.
@pabzm
Copy link
Member Author

pabzm commented Oct 30, 2024

I think in the past there has been issues with defaults.inc.php not being properly updated in 3rd party packaged versions of Roundcube

Oh, I wasn't aware of that. Thank you for that explanation! Then we maybe should have a hardcoded value for all defaults.

I changed the approach to specifying the default value in defaults.inc.php, and having a hardcoded default. And to check that these are in sync, I wrote a test. What do you think about this way?

(I'd be happy to extend this approach to more config options, it would reduce some special case handling in rcube_config, too.)

Personally I just don't like the in_array check, I think its unnecessary because it handles the edge case

I also find it ugly, but I really like solid code, which isn't vulnerable to edge cases. But maybe checking against 'false' is too much. Regarding readability I can turn the check around by using is_string($csp) && strlen($csp) > 0.

Previously the code also treated `'false'` (the string) as invalid, but
that's a very specific check against a specific edge case, which
wouldn't even break the code (but will only make the browser complain),
so I'm dropping that check.
The config value might be something else than a string.
In the previous way useless semicolons could have happened.
We still support PHP v7, which doesn't support union types.
phpstan complains that `assertIsArray($config)` will always fail because
it doesn't know about the side effects of `require`ing the default
config.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Document a working Content-Security-Policy
4 participants